home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / NewsGroupSetBrowser.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  15.2 KB  |  677 lines

  1.  
  2. #import "NewsGroupSetBrowser.h"
  3. #import "Newsgroup.h"
  4. #import "NewsgroupBrowserCell.h"
  5. #import <misckit/MiscAppDefaults.h>
  6. #import "MatrixSet.h"
  7. #import "MatrixScroller.h"
  8. #import "instr.h"
  9. #import "regexpr.h"
  10.  
  11. @implementation NewsGroupSetBrowser
  12.  
  13. #define IS_LEAF 1
  14. #define IS_NO_LEAF 2
  15.  
  16. - awakeFromNib
  17. {
  18.    buttonTitle=NXCopyStringBuffer([selectionButton title]);
  19.    [self setCellPrototype:[[[NewsgroupBrowserCell alloc] init] setNewsgroupCell:nil]];
  20.    return self;
  21. }
  22.  
  23. - free
  24. {
  25.    if(myVisibleCellList!=nil)
  26.       [myVisibleCellList free];
  27.    free(buttonTitle);
  28.    return [super free];
  29. }
  30.  
  31. - initFrame:(const NXRect *)frameRect
  32. {
  33.    int minWidth;
  34.  
  35.    [super initFrame:frameRect];
  36.     
  37.     minWidth=[NXApp defaultIntValue:"MinBrowserColumnWidth"];   
  38.    if(minWidth==0)
  39.       minWidth=70;
  40.  
  41.    [self setAction:@selector(selectNewsgroup:)];
  42.    [self setTarget:mySet];
  43.    [self setPathSeparator:'.'];
  44.    [self setMultipleSelectionEnabled:NO];
  45.    [self setDelegate:self];
  46.    [self setEmptySelectionEnabled:YES];
  47.    [self setHorizontalScrollButtonsEnabled:NO];
  48.    [self setHorizontalScrollerEnabled:YES];
  49.    [self setMinColumnWidth:minWidth];
  50.    [self setTitled:NO];
  51.    [self reuseColumns:YES];
  52.    [self separateColumns:YES];
  53.     [self useScrollBars:YES];
  54.     [self useScrollButtons:YES];
  55.    listList=[[List alloc] init];
  56.  
  57.    return self;
  58. }
  59.  
  60. - (int)browser:sender fillMatrix:matrix inColumn:(int)column
  61. {
  62.    struct bcellDesc{
  63.       int leaf;
  64.       id newsgroup;   
  65.    };
  66.    int i,y,z,from,ii;
  67.    List *aList,*bList,*cList;
  68.    Newsgroup *aNewsgroup;
  69.    const char *name;
  70.    char *end_of_name;
  71.    char *cell_name;
  72.    NXHashState hashState;
  73.    HashTable *aHashTable;
  74.    char *aStringKey;
  75.    char buf[255];
  76.    char *path;
  77.    char isnull[]="";
  78.    struct bcellDesc *c;
  79.  
  80.    //compute column selection list 
  81.    if(column>0){
  82.       //get selected path
  83.       path=[self getPath:buf toColumn:column];
  84.       NX_ASSERT(path!=NULL,"No path selected");
  85.       path++;
  86.  
  87.       y=[listList count];
  88.       for(i=y-1;i>=column;i--)
  89.          [[listList removeObjectAt:i] free];
  90.       bList=[[List alloc] init];
  91.       aList=[listList objectAt:column-1];
  92.       z=[aList count];
  93.       for(i=0;i<z;i++){
  94.          const char *lname;
  95.          int plen=strlen(path);
  96.          aNewsgroup=[aList objectAt:i];
  97.          lname=[aNewsgroup stringValue];
  98.          if((strncmp(lname,path,plen)==0) && (lname[plen]=='.'))
  99.             [bList addObject:aNewsgroup];
  100.       }
  101.       [listList addObject:bList];
  102.    }
  103.    else
  104.       path=isnull;
  105.  
  106.    //compute column entries
  107.    aList=[listList objectAt:column];
  108.    aHashTable=[[HashTable alloc] initKeyDesc:"*" valueDesc:"!"];
  109.    z=[aList count];
  110.    for(i=0;i<z;i++){
  111.       int isLeaf;
  112.       aNewsgroup=[aList objectAt:i];
  113.       name=[aNewsgroup stringValue];
  114.       name+=strlen(path);
  115.       if(path[0]!='\0')
  116.          name++;
  117.    
  118.       end_of_name=strchr(name,'.');
  119.       if(end_of_name==NULL){
  120.          cell_name=NXCopyStringBuffer(name);
  121.          isLeaf=IS_LEAF;
  122.       }
  123.       else{
  124.          int size=strlen(name)-strlen(end_of_name);
  125.          cell_name=(char *)malloc((size+3)*sizeof(char));
  126.          strncpy(cell_name,name,size);
  127.          cell_name[size]='\0';
  128.          isLeaf=IS_NO_LEAF;
  129.       }
  130.       if([aHashTable isKey:(void *)cell_name]){
  131.          c=(struct bcellDesc *)[aHashTable valueForKey:(void *)cell_name];
  132.          free(cell_name);
  133.       }
  134.       else{
  135.          c=(struct bcellDesc *)calloc(1,sizeof(struct bcellDesc));
  136.          [aHashTable insertKey:(const void *)cell_name value:(void *)c];
  137.       }
  138.       c->leaf|=isLeaf;
  139.       if(isLeaf==IS_LEAF)
  140.          c->newsgroup=aNewsgroup;
  141.    }
  142.    
  143.    //column enties are now in the hashtable
  144.    //now create column matrix
  145.    hashState=[aHashTable initState];
  146.    i=-1;
  147.    while([aHashTable nextState:&hashState key:(const void **)&aStringKey value:(void **)&c]){
  148.       id aCell;
  149.       [matrix addRow];
  150.       i++;
  151.       aCell=[matrix cellAt:i :0];
  152.       [aCell setLeaf:((c->leaf&IS_LEAF)==IS_LEAF)];
  153.       [aCell setStringValueNoCopy:aStringKey shouldFree:YES];
  154.       if(c->newsgroup!=nil)
  155.          [aCell setNewsgroupCell:c->newsgroup];
  156.       if(c->leaf==3){
  157.          [matrix addRow];
  158.          i++;
  159.          aCell=[matrix cellAt:i :0];
  160.          [aCell setLeaf:NO];
  161.          [aCell setStringValue:aStringKey];
  162.       }
  163.       free(c);
  164.    }
  165.    i++;
  166.    
  167.    //simple sorting :-(
  168.    cList=[matrix cellList];
  169.    for(from=0;from<i-1;from++){
  170.       int min=from;
  171.       id minObject=[cList objectAt:from];
  172.       const char *minString=[minObject stringValue];      
  173.       for(ii=from+1;ii<i;ii++){
  174.          int c;
  175.          id anObject=[cList objectAt:ii];
  176.          const char *aString=[anObject stringValue];
  177.          if( ((c=strcmp(aString,minString))<0) ||
  178.              (c==0 && [anObject isLeaf]) ){
  179.             minObject=anObject;
  180.             minString=aString;
  181.             min=ii;
  182.          }
  183.       }
  184.       if(min!=from){
  185.          id anObject=[cList replaceObjectAt:from with:[cList objectAt:min]];
  186.          [cList replaceObjectAt:min with:anObject];
  187.       }
  188.    }
  189.          
  190.    [aHashTable free];
  191.    return i;
  192. }
  193.  
  194. - loadMatrix
  195. {
  196.    [self makeVisibleCellList:self];
  197.    [self loadColumnZero];
  198.  
  199.    return self;
  200. }
  201.  
  202. - reloadMatrix
  203. {
  204.    char path_buf[255];
  205.    id curSel;
  206.  
  207.    [[self window] disableFlushWindow];
  208.    [self setAutodisplay:NO];
  209.  
  210.    curSel=[mySet currentSelection];
  211.  
  212.    [self getPath:path_buf toColumn:[self lastColumn]];
  213.    
  214.    [self loadMatrix];
  215.  
  216.    //reselect path or path+cell
  217.    if(curSel==nil){
  218.       if(path_buf[0]!='\0'){
  219.          int i,j,z;
  220.          z=strlen(path_buf)-1;
  221.          j=[myVisibleCellList count];
  222.          //see if there's a visible group with its prefix==path_buf 
  223.          for(i=0;i<j;i++)
  224.             if(strncmp([[myVisibleCellList objectAt:i] stringValue],path_buf+1,z)==0){
  225.                strcat(path_buf,".");
  226.                [self setPath:path_buf];
  227.                break;
  228.             }
  229.       }
  230.    }
  231.    else if([curSel isTaged]==TRUE){
  232.       const char *gname=[curSel stringValue];
  233.       char *newPath;
  234.  
  235.       newPath=(char *)malloc((strlen(gname)+2)*sizeof(char));
  236.       sprintf(newPath,".%s",gname);
  237.       [self setPath:newPath];
  238.       free(newPath);
  239.    }
  240.  
  241.    [[self setAutodisplay:YES] display];
  242.    [[[self window] reenableFlushWindow] flushWindow];
  243.    return self;
  244. }
  245.  
  246. - setMatrixCellList:(List *)aList
  247. {
  248.    myCellList=aList;
  249.    return self;
  250. }
  251.  
  252.  
  253. - (List *)cellList
  254. {
  255.    return myVisibleCellList;
  256. }
  257.  
  258. - removeInvalidCell:aCell;
  259. {
  260.    return [self removeInvalidCell:aCell andUpdate:YES];
  261. }
  262.  
  263. - removeInvalidCell:aCell andUpdate:(BOOL)update
  264. {
  265.    int i,j;
  266.    for(i=0,j=[listList count];i<j;i++)
  267.       [[listList objectAt:i] removeObject:aCell];
  268.    [myVisibleCellList removeObject:aCell];
  269.    [[myCellList removeObject:aCell] free];
  270.  
  271.    if(update==YES)
  272.       [self update];
  273.  
  274.    return self;
  275. }
  276.  
  277. - update
  278. {
  279.    [self loadColumnZero];
  280.    [self display];
  281.    [mySet sync];
  282.    [NXApp updateWindows];
  283.  
  284.    return self;
  285. }
  286.  
  287. - (List *)getCurrSelections
  288. {
  289.    List *aList=[[List alloc] init];
  290.    id selection;
  291.  
  292.    selection=[self currSelection];
  293.    if(selection!=nil)
  294.       [aList addObject:selection];
  295.    else {
  296.       int j=[listList count];
  297.       NX_ASSERT(j>0,"Listlist empty");
  298.       if(j>1)
  299.          if([[listList objectAt:j-1] count]>1) //shit! 
  300.             [aList appendList:[listList objectAt:j-1]];
  301.    }
  302.    return aList;
  303. }
  304.    
  305.  
  306. - currSelection
  307. {
  308.    char buf[255];
  309.    char *newsg_name;
  310.    id aCell=[self selectedCell];
  311.    id selCell=nil;
  312.    List *aList;
  313.    int i,j;
  314.  
  315.    if(aCell==nil)
  316.       return nil;
  317.    if([aCell isLeaf]==TRUE){
  318.       const char *cellName=[aCell stringValue];
  319.       int lcol=[self lastColumn];
  320.       [self getPath:buf toColumn:lcol];
  321.       
  322.       newsg_name=(char *)malloc((strlen(buf)+strlen(cellName)+3)*sizeof(char));
  323.       newsg_name[0]='\0';
  324.       strcpy(newsg_name,buf);
  325.       strcat(newsg_name,".");
  326.       strcat(newsg_name,cellName);
  327.  
  328.       aList=[listList objectAt:lcol];
  329.       j=[aList count];
  330.       for(i=0;i<j;i++){
  331.          if(strcmp([[aList objectAt:i] stringValue],newsg_name+1)==0){
  332.             selCell=[aList objectAt:i];
  333.             break;
  334.          }
  335.       }
  336.       free(newsg_name);
  337.    }
  338.    return selCell;
  339. }
  340.  
  341. - makeVisibleCellList:sender
  342. {
  343.    int i,j;
  344.  
  345.    if(myVisibleCellList!=nil)
  346.       [myVisibleCellList free];
  347.    myVisibleCellList=[[List alloc] init];
  348.    j=[myCellList count];
  349.    for(i=0;i<j;i++){
  350.       id aCell=[myCellList objectAt:i];
  351.       if([aCell isTaged]==TRUE)
  352.          [myVisibleCellList addObject:aCell];
  353.    }
  354.  
  355.    if([listList count]==0)
  356.       [listList addObject:myVisibleCellList];
  357.    else
  358.       [listList replaceObjectAt:0 with:myVisibleCellList];
  359.  
  360.    return self;
  361. }
  362.  
  363. - (id)selectionButton
  364. {
  365.    return selectionButton;
  366. }
  367.  
  368. - setButtonTitle:(const char *)title
  369. {
  370.    free(buttonTitle);
  371.    [selectionButton setTitle:title];
  372.    buttonTitle=NXCopyStringBuffer(title);
  373.  
  374.    return self;
  375. }
  376.  
  377. - _delayedUpdate:sender
  378. {
  379.    [selectionButton setTitle:buttonTitle];
  380.    return self;
  381. }
  382.  
  383. - updateButtonTitle
  384. {
  385.    [self perform:@selector(_delayedUpdate:) with:self afterDelay:0.0 cancelPrevious:TRUE];  
  386.    return self;
  387. }
  388.  
  389. - (BOOL)selectNextCell:(int)delta inColumn:(int)col leafSelected:(BOOL *)sel
  390. {
  391.    id matrix=[self matrixInColumn:col];
  392.    id aCell=[matrix selectedCell];
  393.    id cellList=[matrix cellList];
  394.    int j,jj;
  395.    int i=0;
  396.    char buf[255];
  397.    id newCell;
  398.  
  399.    [matrix getNumRows:&j numCols:&jj];
  400.  
  401.    *sel=FALSE;
  402.    if(j==0)
  403.       return FALSE;
  404.    //find index of cell to select
  405.    if(aCell==nil){
  406.       if(delta>0)
  407.          i=0;
  408.       else
  409.          i=j-1;
  410.    }
  411.    else{
  412.       int pos=[cellList indexOf:aCell];
  413.       if(((delta>0)&&(pos==j-1))||((delta<0)&&(pos==0)))
  414.          return FALSE; 
  415.       i=pos+delta;
  416.    }
  417.  
  418.    //compose and set path
  419.    [self getPath:buf toColumn:col];
  420.    newCell=[cellList objectAt:i];
  421.    if(![newCell isLeaf]){
  422.       strcat(buf,".");
  423.       strcat(buf,[newCell stringValue]);
  424.       strcat(buf,".");
  425.       [self setPath:buf];
  426.    }
  427.    else{
  428.       strcat(buf,".");
  429.       strcat(buf,[newCell stringValue]);
  430.       [self setPath:buf];
  431.       *sel=TRUE;
  432.    }
  433.  
  434.    return TRUE;
  435. }
  436.  
  437. - (BOOL)selectNextCell:(int)delta
  438. {
  439.    int i;
  440.    BOOL r,rr;
  441.  
  442.    NX_ASSERT(((delta==1) || (delta==-1)),"Wrong parameter selectNextCell");
  443.    
  444.    for(i=[self lastColumn];i>=0;i--){
  445.       r=[self selectNextCell:delta inColumn:i leafSelected:&rr];
  446.       if(rr==TRUE){
  447.          [self sendAction];
  448.          return TRUE;
  449.       }
  450.       if(r==TRUE)
  451.          i=[self lastColumn]+1;
  452.    }
  453.  
  454.    return FALSE;
  455. }
  456.        
  457. - setPath:(const char *)aPath
  458. {
  459.    char *newPath,*c;
  460.    id bmatrix;
  461.    int i,j;
  462.  
  463.    NX_ASSERT(*aPath==(char)pathSeparator,"setPath: wrong pathstring");
  464.  
  465.    if(aPath[strlen(aPath)-1]==(char)pathSeparator){
  466.       if([self selectedCell]!=nil)
  467.          [[self matrixInColumn:[self lastColumn]] selectCellAt:-1 :-1];
  468.       return [super setPath:aPath];
  469.    }
  470.  
  471.    newPath=NXCopyStringBuffer(aPath);
  472.  
  473.    c=strrchr(newPath,pathSeparator);
  474.    NX_ASSERT(c!=NULL,"setPath: internal error");
  475.  
  476.    *c='\0';
  477.    if(*newPath=='\0')
  478.       [super setPath:"."];
  479.    else if([super setPath:newPath]==nil)
  480.       return nil;
  481.    c++;
  482.  
  483.    bmatrix=[self matrixInColumn:[self lastColumn]];
  484.    [bmatrix getNumRows:&j numCols:&i];
  485.    for(i=0;i<j;i++){
  486.       id aCell=[bmatrix cellAt:i :0];
  487.       if(!strcmp([aCell stringValue],c))
  488.          if([aCell isLeaf]){
  489.             [bmatrix selectCellAt:i :0];
  490.             break;
  491.          }
  492.    }
  493.    free(newPath);
  494.    [self scrollColumnToVisible:[self lastColumn]];
  495.    [self scrollSelectionToVisible];
  496.  
  497.    if(i==j)
  498.       return nil;
  499.    return self;
  500. }
  501.  
  502. - scrollSelectionToVisible
  503. {
  504.    id aMatrix=[self matrixInColumn:[self lastVisibleColumn]];
  505.    id aCell=[aMatrix selectedCell];
  506.    if(aCell!=nil)
  507.       [aMatrix scrollCellToVisible:[[aMatrix cellList] indexOf:aCell]
  508.                upperOffset:1.5 lowerOffset:1.5];
  509.    
  510.    return self;
  511. }
  512. //-------------------------
  513. // searchable text protocol
  514. //-------------------------
  515.  
  516. - (oneway void)makeSelectionVisible
  517. {
  518. }
  519.  
  520. - (int)replaceAll:(const char *)pattern with:(const char *)replacement mode:(SearchMode)mode regexpr:(BOOL)regexpr cases:(BOOL)cases
  521. {
  522.    return SEARCH_CANNOT_WRITE;
  523. }
  524.  
  525. - (oneway void)replaceSelection:(const char *)replacement
  526. {
  527. }
  528.  
  529. - (const char *)stringValueForCellAt:(int)index
  530. {
  531.    return [[myVisibleCellList objectAt:index] stringValue];
  532. }
  533.  
  534. - (int)searchFor:(const char *)pattern mode:(SearchMode)mode reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(out int *)pos size:(out int *)size
  535. {
  536.    int ccount;
  537.    int s_pos;
  538.    int fStart,fEnd;
  539.    int delta;
  540.    List *selList;
  541.  
  542.    unsigned char fm[256], tr[256];
  543.    struct re_pattern_buffer rpat;
  544.  
  545.    s_pos=-2;
  546.    fStart=fEnd=-2;
  547.  
  548.    ccount=[myVisibleCellList count];
  549.    if(ccount==0)
  550.       return 0;
  551.  
  552.    // find first selected cell
  553.    selList=[self getCurrSelections];
  554.    if([selList count]>0)
  555.       s_pos=[myVisibleCellList indexOf:[selList objectAt:0]];
  556.    [selList free];
  557.  
  558.    if(!rev){
  559.       if(s_pos<0){ 
  560.          fStart=0; fEnd=ccount; 
  561.       }
  562.       else{
  563.          fStart=s_pos+1; fEnd=s_pos+ccount; 
  564.       }
  565.    }
  566.    else{
  567.       if(s_pos<0){ 
  568.          fStart=ccount-1; fEnd=-1; 
  569.       }
  570.       else{
  571.          fStart=s_pos-1+ccount; fEnd=s_pos+1; 
  572.       }
  573.    }
  574.    
  575.    delta= rev? -1:1;
  576.   
  577.    if(regexpr){
  578.       char *str;
  579.       int i;
  580.  
  581.       memset(&rpat, 0, sizeof(rpat));
  582.       for(i=256; i--;)
  583.          tr[i] = i;
  584.       if(!cases)
  585.          for(i='A'; i<='Z'; i++)
  586.             tr[i] = i-'A'+'a';
  587.       rpat.translate = tr;
  588.       rpat.fastmap = fm;
  589.       str = re_compile_pattern((char *)pattern,strlen(pattern), &rpat);
  590.       if (str!=NULL)
  591.         return (strcmp(str, "Out of memory")?SEARCH_INVALID_REGEXPR:SEARCH_INTERNAL_ERROR);
  592.    }
  593.  
  594.    for(;fStart!=fEnd;fStart+=delta){
  595.       int index=fStart%ccount;
  596.       const char *result=NULL;
  597.       const char *cellString=[self stringValueForCellAt:index];
  598.  
  599.       if(regexpr){
  600.         int l=strlen(cellString);
  601.         int p=re_search_pattern(&rpat,(char *)cellString,l,0,l,0);
  602.  
  603.         if(p==-2)
  604.            return SEARCH_INTERNAL_ERROR;
  605.         result= (p==-1)? NULL : cellString;
  606.       }
  607.       else
  608.          result=instr(cellString,pattern,cases);
  609.  
  610.       if(result!=NULL){
  611.          *pos=index;
  612.          *size=1;
  613.          return 1;
  614.       }
  615.    }
  616.    
  617.    return 0;
  618. }
  619.  
  620. - (oneway void)selectTextFrom:(int)start to:(int)end
  621. {
  622.    if(start<=end){
  623.       if(start==end){
  624.          [super setPath:"."];
  625.          [self sendAction];
  626.       }
  627.       else{
  628.          if(start<[myVisibleCellList count]){
  629.             id aCell=[myVisibleCellList objectAt:start];
  630.             char *newPath=malloc((strlen([aCell stringValue])+2)*sizeof(char));
  631.             sprintf(newPath,".%s",[aCell stringValue]);
  632.             [self setPath:newPath];
  633.             free(newPath);
  634.             [self sendAction];
  635.          }
  636.       }
  637.    }
  638. }
  639.       
  640. - (void)writeSelectionToPasteboard:(in Pasteboard *)pboard asType:(in NXAtom)type
  641. {
  642.    id aCell=[self currSelection];
  643.  
  644.    if(aCell!=nil){
  645.       const char *sval=[aCell stringValue];
  646.       [pboard declareTypes:&type num:1 owner:NULL];
  647.       [pboard writeType:type data:sval length:strlen(sval)];
  648.    }
  649.    return;
  650. }
  651.  
  652. - mySet
  653. {
  654.    return mySet;
  655. }
  656.  
  657. - (BOOL)selectNewsgroupNamed:(const char *)gname
  658. {
  659.     int i,j;
  660.     for(i=0,j=[myVisibleCellList count];i<j;i++){        
  661.         const char *name=[[myVisibleCellList objectAt:i] stringValue];
  662.         if(name && !strcmp(gname,name)){
  663.           char *newPath=malloc((strlen(name)+2)*sizeof(char));
  664.          sprintf(newPath,".%s",name);
  665.          [self setPath:newPath];
  666.             [self scrollSelectionToVisible];
  667.          free(newPath);
  668.          [self sendAction];
  669.             
  670.             return TRUE;
  671.       }
  672.     }
  673.     return FALSE;
  674. }
  675.  
  676. @end
  677.